/*
 * Activiti Modeler component part of the Activiti project
 * Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
'use strict';

var activitiModeler = angular.module('activitiModeler', [
    'ngCookies',
    'ngResource',
    'ngSanitize',
    'ngRoute',
    'ngDragDrop',
    'mgcrea.ngStrap',
    'ngGrid',
    'ngAnimate',
    'pascalprecht.translate',
    'duScroll'
]);

var activitiModule = activitiModeler;

activitiModeler
    // Initialize routes
    .config(['$selectProvider', '$translateProvider', function ($selectProvider, $translateProvider) {

        // Override caret for bs-select directive
        angular.extend($selectProvider.defaults, {
            caretHtml: '&nbsp;<i class="icon icon-caret-down"></i>'
        });

        // Initialize angular-translate
        $translateProvider.useStaticFilesLoader({
            prefix: '../../editor-app/i18n/',
            suffix: '.json'
        });

        $translateProvider.preferredLanguage('en');

        // remember language
        $translateProvider.useCookieStorage();

    }])
    .run(['$rootScope', '$timeout', '$modal', '$translate', '$location', '$window', '$http', '$q',
        function ($rootScope, $timeout, $modal, $translate, $location, $window, $http, $q) {

            $rootScope.config = ACTIVITI.CONFIG;

            $rootScope.editorInitialized = false;

            $rootScope.editorFactory = $q.defer();

            $rootScope.forceSelectionRefresh = false;

            $rootScope.ignoreChanges = false; // by default never ignore changes

            $rootScope.validationErrors = [];

            $rootScope.staticIncludeVersion = Date.now();

            /**
             * A 'safer' apply that avoids concurrent updates (which $apply allows).
             */
            $rootScope.safeApply = function (fn) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            };


            /**
             * Initialize the event bus: couple all Oryx events with a dispatch of the
             * event of the event bus. This way, it gets much easier to attach custom logic
             * to any event.
             */

            /* Helper method to fetch model from server (always needed) */
            function fetchModel(modelId) {

                var modelUrl = KISBPM.URL.getModel(modelId);

                $http({method: 'GET', url: modelUrl}).success(function (data, status, headers, config) {
                    $rootScope.editor = new ORYX.Editor(data);
                    $rootScope.modelData = angular.fromJson(data);
                    $rootScope.editorFactory.resolve();
                }).error(function (data, status, headers, config) {
                    console.log('Error loading model with id ' + modelId + ' ' + data);
                });
            }


            function initScrollHandling() {
                var canvasSection = jQuery('#canvasSection');
                canvasSection.scroll(function () {

                    // Hides the resizer and quick menu items during scrolling

                    var selectedElements = $rootScope.editor.selection;
                    var subSelectionElements = $rootScope.editor._subSelection;

                    $rootScope.selectedElements = selectedElements;
                    $rootScope.subSelectionElements = subSelectionElements;
                    if (selectedElements && selectedElements.length > 0) {
                        $rootScope.selectedElementBeforeScrolling = selectedElements[0];
                    }

                    jQuery('.Oryx_button').each(function (i, obj) {
                        $rootScope.orginalOryxButtonStyle = obj.style.display;
                        obj.style.display = 'none';
                    });

                    jQuery('.resizer_southeast').each(function (i, obj) {
                        $rootScope.orginalResizerSEStyle = obj.style.display;
                        obj.style.display = 'none';
                    });
                    jQuery('.resizer_northwest').each(function (i, obj) {
                        $rootScope.orginalResizerNWStyle = obj.style.display;
                        obj.style.display = 'none';
                    });
                    $rootScope.editor.handleEvents({type: ORYX.CONFIG.EVENT_CANVAS_SCROLL});
                });

                canvasSection.scrollStopped(function () {

                    // Puts the quick menu items and resizer back when scroll is stopped.

                    $rootScope.editor.setSelection([]); // needed cause it checks for element changes and does nothing if the elements are the same
                    $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements);
                    $rootScope.selectedElements = undefined;
                    $rootScope.subSelectionElements = undefined;

                    function handleDisplayProperty(obj) {
                        if (jQuery(obj).position().top > 0) {
                            obj.style.display = 'block';
                        } else {
                            obj.style.display = 'none';
                        }
                    }

                    jQuery('.Oryx_button').each(function (i, obj) {
                        handleDisplayProperty(obj);
                    });

                    jQuery('.resizer_southeast').each(function (i, obj) {
                        handleDisplayProperty(obj);
                    });
                    jQuery('.resizer_northwest').each(function (i, obj) {
                        handleDisplayProperty(obj);
                    });

                });
            }

            /**
             * Initialize the Oryx Editor when the content has been loaded
             */
            $rootScope.$on('$includeContentLoaded', function (event) {
                if (!$rootScope.editorInitialized) {

                    ORYX._loadPlugins();

                    var modelId = EDITOR.UTIL.getParameterByName('modelId');
                    fetchModel(modelId);

                    $rootScope.window = {};
                    var updateWindowSize = function () {
                        $rootScope.window.width = $window.innerWidth;
                        $rootScope.window.height = $window.innerHeight;
                    };

                    // Window resize hook
                    angular.element($window).bind('resize', function () {
                        $rootScope.safeApply(updateWindowSize());
                    });

                    $rootScope.$watch('window.forceRefresh', function (newValue) {
                        if (newValue) {
                            $timeout(function () {
                                updateWindowSize();
                                $rootScope.window.forceRefresh = false;
                            });
                        }
                    });

                    updateWindowSize();

                    // Hook in resizing of main panels when window resizes
                    // TODO: perhaps move to a separate JS-file?
                    jQuery(window).resize(function () {

                        // Calculate the offset based on the bottom of the module header
                        var offset = jQuery("#editor-header").offset();
                        var propSectionHeight = jQuery('#propertySection').height();
                        var canvas = jQuery('#canvasSection');
                        var mainHeader = jQuery('#main-header');

                        if (offset == undefined || offset === null
                            || propSectionHeight === undefined || propSectionHeight === null
                            || canvas === undefined || canvas === null || mainHeader === null) {
                            return;
                        }

                        if ($rootScope.editor) {
                            var selectedElements = $rootScope.editor.selection;
                            var subSelectionElements = $rootScope.editor._subSelection;

                            $rootScope.selectedElements = selectedElements;
                            $rootScope.subSelectionElements = subSelectionElements;
                            if (selectedElements && selectedElements.length > 0) {
                                $rootScope.selectedElementBeforeScrolling = selectedElements[0];

                                $rootScope.editor.setSelection([]); // needed cause it checks for element changes and does nothing if the elements are the same
                                $rootScope.editor.setSelection($rootScope.selectedElements, $rootScope.subSelectionElements);
                                $rootScope.selectedElements = undefined;
                                $rootScope.subSelectionElements = undefined;
                            }
                        }

                        var totalAvailable = jQuery(window).height() - offset.top - mainHeader.height() - 21;
                        canvas.height(totalAvailable - propSectionHeight);
                        jQuery('#paletteSection').height(totalAvailable);

                        // Update positions of the resize-markers, according to the canvas

                        var actualCanvas = null;
                        if (canvas && canvas[0].children[1]) {
                            actualCanvas = canvas[0].children[1];
                        }

                        var canvasTop = canvas.position().top;
                        var canvasLeft = canvas.position().left;
                        var canvasHeight = canvas[0].clientHeight;
                        var canvasWidth = canvas[0].clientWidth;
                        var iconCenterOffset = 8;
                        var widthDiff = 0;

                        var actualWidth = 0;
                        if (actualCanvas) {
                            // In some browsers, the SVG-element clientwidth isn't available, so we revert to the parent
                            actualWidth = actualCanvas.clientWidth || actualCanvas.parentNode.clientWidth;
                        }


                        if (actualWidth < canvas[0].clientWidth) {
                            widthDiff = actualWidth - canvas[0].clientWidth;
                            // In case the canvas is smaller than the actual viewport, the resizers should be moved
                            canvasLeft -= widthDiff / 2;
                            canvasWidth += widthDiff;
                        }

                        var iconWidth = 17;
                        var iconOffset = 20;

                        var north = jQuery('#canvas-grow-N');
                        north.css('top', canvasTop + iconOffset + 'px');
                        north.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px');

                        var south = jQuery('#canvas-grow-S');
                        south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px');
                        south.css('left', canvasLeft - 10 + (canvasWidth - iconWidth) / 2 + 'px');

                        var east = jQuery('#canvas-grow-E');
                        east.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px');
                        east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px');

                        var west = jQuery('#canvas-grow-W');
                        west.css('top', canvasTop - 10 + (canvasHeight - iconWidth) / 2 + 'px');
                        west.css('left', canvasLeft + iconOffset + 'px');

                        north = jQuery('#canvas-shrink-N');
                        north.css('top', canvasTop + iconOffset + 'px');
                        north.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px');

                        south = jQuery('#canvas-shrink-S');
                        south.css('top', (canvasTop + canvasHeight - iconOffset - iconCenterOffset) + 'px');
                        south.css('left', canvasLeft + 10 + (canvasWidth - iconWidth) / 2 + 'px');

                        east = jQuery('#canvas-shrink-E');
                        east.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px');
                        east.css('left', (canvasLeft + canvasWidth - iconOffset - iconCenterOffset) + 'px');

                        west = jQuery('#canvas-shrink-W');
                        west.css('top', canvasTop + 10 + (canvasHeight - iconWidth) / 2 + 'px');
                        west.css('left', canvasLeft + iconOffset + 'px');
                    });

                    jQuery(window).trigger('resize');

                    jQuery.fn.scrollStopped = function (callback) {
                        jQuery(this).scroll(function () {
                            var self = this, $this = jQuery(self);
                            if ($this.data('scrollTimeout')) {
                                clearTimeout($this.data('scrollTimeout'));
                            }
                            $this.data('scrollTimeout', setTimeout(callback, 50, self));
                        });
                    };

                    // Always needed, cause the DOM element on which the scroll event listeners are attached are changed for every new model
                    initScrollHandling();

                    $rootScope.editorInitialized = true;
                }
            });

            /**
             * Initialize the event bus: couple all Oryx events with a dispatch of the
             * event of the event bus. This way, it gets much easier to attach custom logic
             * to any event.
             */

            $rootScope.editorFactory.promise.then(function () {

                KISBPM.eventBus.editor = $rootScope.editor;

                var eventMappings = [
                    {
                        oryxType: ORYX.CONFIG.EVENT_SELECTION_CHANGED,
                        kisBpmType: KISBPM.eventBus.EVENT_TYPE_SELECTION_CHANGE
                    },
                    {oryxType: ORYX.CONFIG.EVENT_DBLCLICK, kisBpmType: KISBPM.eventBus.EVENT_TYPE_DOUBLE_CLICK},
                    {oryxType: ORYX.CONFIG.EVENT_MOUSEOUT, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OUT},
                    {oryxType: ORYX.CONFIG.EVENT_MOUSEOVER, kisBpmType: KISBPM.eventBus.EVENT_TYPE_MOUSE_OVER}

                ];

                eventMappings.forEach(function (eventMapping) {
                    $rootScope.editor.registerOnEvent(eventMapping.oryxType, function (event) {
                        KISBPM.eventBus.dispatch(eventMapping.kisBpmType, event);
                    });
                });

                $rootScope.editor.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, function (event) {
                    var validateButton = document.getElementById(event.shape.resourceId + "-validate-button");
                    if (validateButton) {
                        validateButton.style.display = 'none';
                    }
                });

                // The Oryx canvas is ready (we know since we're in this promise callback) and the
                // event bus is ready. The editor is now ready for use
                KISBPM.eventBus.dispatch(KISBPM.eventBus.EVENT_TYPE_EDITOR_READY, {type: KISBPM.eventBus.EVENT_TYPE_EDITOR_READY});

            });

            // Alerts
            $rootScope.alerts = {
                queue: []
            };

            $rootScope.showAlert = function (alert) {
                if (alert.queue.length > 0) {
                    alert.current = alert.queue.shift();
                    // Start timout for message-pruning
                    alert.timeout = $timeout(function () {
                        if (alert.queue.length == 0) {
                            alert.current = undefined;
                            alert.timeout = undefined;
                        } else {
                            $rootScope.showAlert(alert);
                        }
                    }, (alert.current.type == 'error' ? 5000 : 1000));
                } else {
                    $rootScope.alerts.current = undefined;
                }
            };

            $rootScope.addAlert = function (message, type) {
                var newAlert = {message: message, type: type};
                if (!$rootScope.alerts.timeout) {
                    // Timeout for message queue is not running, start one
                    $rootScope.alerts.queue.push(newAlert);
                    $rootScope.showAlert($rootScope.alerts);
                } else {
                    $rootScope.alerts.queue.push(newAlert);
                }
            };

            $rootScope.dismissAlert = function () {
                if (!$rootScope.alerts.timeout) {
                    $rootScope.alerts.current = undefined;
                } else {
                    $timeout.cancel($rootScope.alerts.timeout);
                    $rootScope.alerts.timeout = undefined;
                    $rootScope.showAlert($rootScope.alerts);
                }
            };

            $rootScope.addAlertPromise = function (promise, type) {
                if (promise) {
                    promise.then(function (data) {
                        $rootScope.addAlert(data, type);
                    });
                }
            };

        }
    ])

    // Moment-JS date-formatting filter
    .filter('dateformat', function () {
        return function (date, format) {
            if (date) {
                if (format) {
                    return moment(date).format(format);
                } else {
                    return moment(date).calendar();
                }
            }
            return '';
        };
    });
